#include "main.h"
#include "flash.h"
#include "stm32_hal_legacy.h"
#include <string.h>
#include "stdio.h"

// ??Flash??
struct FlashData Flashdata = {0};

/*
const FlashConfig flash_cfg = {
    .start_address = 0x08000000,  // Flash????
    .end_address = 0x08020000,    // Flash????(128KB)
    .sector_size = 0x800,         // 2KB????
    .page_size = 0                 // ??F1????????
};
static FlashConfig flash_config = {0};

// ???Flash??
FlashStatus Flash_Init(const FlashConfig *config) {
    if (config == NULL || 
        config->start_address == 0 || 
        config->end_address <= config->start_address) {
        return FLASH_INVALID_ADDRESS;
    }
    
    memcpy(&flash_config, config, sizeof(FlashConfig));
    
    // ??Flash
    HAL_FLASH_Unlock();
    
    // ????????
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPERR);
    
    return FLASH_OK;
}

// ??????
FlashStatus Flash_EraseSector(uint32_t sector_index) {
    if (flash_config.sector_size == 0) {
        return FLASH_ERROR;
    }
    
    uint32_t sector_start = Flash_GetSectorStartAddress(sector_index);
    if (sector_start < flash_config.start_address || 
        sector_start >= flash_config.end_address) {
        return FLASH_INVALID_ADDRESS;
    }
    
    FLASH_EraseInitTypeDef erase_init;
    uint32_t sector_error;
    
#if defined(STM32F1)
    // STM32F1????
    erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
    erase_init.PageAddress = sector_start;
    erase_init.NbPages = 1;
#elif defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
    // STM32F4/F7/H7????
    erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase_init.Sector = sector_index;
    erase_init.NbSectors = 1;
    erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3; // ????????
#elif defined(STM32L4) || defined(STM32L0) || defined(STM32G0)
    // STM32L4/L0????
    erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
    erase_init.Page = sector_index;
    erase_init.NbPages = 1;
#else
    #error "Unsupported STM32 series"
#endif

    erase_init.Banks = FLASH_BANK_1; // ???Bank????????
    
    if (HAL_FLASHEx_Erase(&erase_init, &sector_error) != HAL_OK) {
        return FLASH_ERASE_FAILED;
    }
    
    return FLASH_OK;
}

// ????????
FlashStatus Flash_EraseRange(uint32_t start_addr, uint32_t length) {
    if (start_addr < flash_config.start_address || 
        start_addr + length > flash_config.end_address) {
        return FLASH_INVALID_ADDRESS;
    }
    
    uint32_t start_sector = Flash_GetSectorIndex(start_addr);
    uint32_t end_addr = start_addr + length - 1;
    uint32_t end_sector = Flash_GetSectorIndex(end_addr);
    
    for (uint32_t sector = start_sector; sector <= end_sector; sector++) {
        FlashStatus status = Flash_EraseSector(sector);
        if (status != FLASH_OK) {
            return status;
        }
    }
    
    return FLASH_OK;
}


// ??????
uint32_t Flash_GetSectorIndex(uint32_t address) {
    if (address < flash_config.start_address || address >= flash_config.end_address) {
        return 0xFFFFFFFF; // ????
    }
    
    uint32_t offset = address - flash_config.start_address;
    return offset / flash_config.sector_size;
}


// ????????
uint32_t Flash_GetSectorStartAddress(uint32_t sector_index) {
    uint32_t max_sectors = (flash_config.end_address - flash_config.start_address) / flash_config.sector_size;
    
    if (sector_index >= max_sectors) {
        return 0; // ??????
    }
    
    return flash_config.start_address + (sector_index * flash_config.sector_size);
}

// ???????????
uint8_t Flash_IsErased(uint32_t address, uint32_t length) {
    for (uint32_t i = 0; i < length; i++) {
        //if (Flash_ReadByte(address + i) != 0xFF) {
        //    return 0; // ?????
        //}
    }
    return 1; // ???
}
*/

// ????64???
void Flash_Write_64bit(uint32_t addr, uint64_t data) {
    // ??????8????
    if ((addr & 0x7) != 0) {
        // ?????????
			//printf("wtf:%X addr is not ok.\r\n", addr);
      return;
    }
    
    // ??Flash??
		HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, data);
		if ( status != HAL_OK) {
        // ????
			printf("flash data not ok: %d..\r\n", status);
			uint32_t error = HAL_FLASH_GetError();

			if (error & HAL_FLASH_ERROR_PROG) {
					// Programming error (alignment, voltage)
				printf("HAL_FLASH_ERROR_PROG\r\n");
			}
			if (error & HAL_FLASH_ERROR_WRP) {
					// Write protection error (option bytes)
				printf("HAL_FLASH_ERROR_WRP\r\n");
			}
			if (error & HAL_FLASH_ERROR_FAST) {
					// Fast programming error
				printf("HAL_FLASH_ERROR_FAST\r\n");
			}
			if (error & HAL_FLASH_ERROR_OPTV) {
					// Option validity error
				printf("HAL_FLASH_ERROR_OPTV\r\n");
			}
			if (error & HAL_FLASH_ERROR_ECCD) {
					// ECC double detection error
				printf("HAL_FLASH_ERROR_ECCD\r\n");
			}
    } else {
			//printf("flash program ok...\r\n");
		}
}

// ??????Flash
void Flash_Write_String(const char* str, uint32_t addr) {
    uint32_t len = strlen(str);
    uint32_t full_blocks = len / 8;
    uint8_t remainder = len % 8;
    
    // ??Flash
    HAL_FLASH_Unlock();
		//printf("full_blocks:%d remainder: %d\r\n", full_blocks, remainder);
    // ?????64??
    for (uint32_t i = 0; i < full_blocks; i++) {
        uint64_t data = 0;
        memcpy(&data, str + i * 8, 8);
				//printf("write data: %d\r\n", str[i]);
        Flash_Write_64bit(addr + i * 8, data);
    }
    
    // ??????(??8?????)
    if (remainder > 0) {
        uint64_t last_data = 0x0; // ????????(0x0)
        memcpy(&last_data, str + full_blocks * 8, remainder);
				//printf("write str:%s\r\n", str);
        Flash_Write_64bit(addr + full_blocks * 8, last_data);
    }
        
    // ??Flash
    HAL_FLASH_Lock();
}


// ?Flash?????
char* Flash_Read_String(char *buffer, uint32_t addr, uint32_t max_len) {
    // ????(?????????????)
    //static char buffer[256]; 
    uint32_t i = 0;
    
    // ?Flash??????????????????
    for (i = 0; i < max_len - 1; i++) {
        buffer[i] = *(volatile uint8_t*)(addr + i);
        if (buffer[i] == '\0') break;
    }
    
    buffer[i] = '\0'; // ???????
    return buffer;
}

// ??Flash??????RAM?????
int String_Compare(const char* flash_str, const char* ram_str) {
    while (*flash_str && *ram_str) {
        if (*flash_str != *ram_str) {
            return 0; // ???
        }
        flash_str++;
        ram_str++;
    }
    // ????????
    return (*flash_str == '\0' && *ram_str == '\0');
}

void Flash_Write_Byte(const uint8_t* data, uint32_t addr, uint32_t len) {
    uint32_t full_blocks = len / 8;
    uint8_t remainder = len % 8;
    
    // ??Flash
    HAL_FLASH_Unlock();
		//printf("full_blocks:%d remainder: %d\r\n", full_blocks, remainder);
    // ?????64??
    for (uint32_t i = 0; i < full_blocks; i++) {
        uint64_t tmp = 0;
        memcpy(&tmp, data + i * 8, 8);
				//printf("write data: %d\r\n", str[i]);
        Flash_Write_64bit(addr + i * 8, tmp);
    }
    
    // ??????(??8?????)
    if (remainder > 0) {
        uint64_t last_data = 0x0; // ????????(0x0)
        memcpy(&last_data, data + full_blocks * 8, remainder);
				//printf("write str:%s\r\n", str);
        Flash_Write_64bit(addr + full_blocks * 8, last_data);
    }
        
    // ??Flash
    HAL_FLASH_Lock();
}

static uint8_t* Flash_Read_Byte(uint8_t *buffer, uint32_t addr, uint32_t max_len) {
    uint32_t i = 0;
    
    for (i = 0; i < max_len - 1; i++) {
        buffer[i] = *(volatile uint8_t*)(addr + i);
    }
    
    return buffer;
}

static uint32_t GetPage(uint32_t Addr)
{
  return (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;;
}

HAL_StatusTypeDef FlashErase(uint32_t addr)
{
		uint32_t FirstPage = 0, NbOfPages = 0;
		uint32_t PageError = 0;
		
		FLASH_EraseInitTypeDef EraseInitStruct;
		FirstPage = GetPage(FLASH_USER_PAGE_ADDR);
		/* Get the number of pages to erase from 1st page */
		NbOfPages = GetPage(addr) - FirstPage + 1;

		//printf("page: %d page: %d\r\n", FirstPage, NbOfPages);
		/* Fill EraseInit structure*/
		EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
		EraseInitStruct.Page        = FirstPage;
		EraseInitStruct.NbPages     = NbOfPages;
	  if (HAL_FLASH_Unlock() != HAL_OK) {
       return HAL_ERROR;
    }

		if (HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) != HAL_OK)
		{
			/*
				Error occurred while page erase.
				User can add here some code to deal with this error.
				PageError will contain the faulty page and then to know the code error on this page,
				user can call function 'HAL_FLASH_GetError()'
			*/
			/* Infinite loop */
			return HAL_ERROR;
			//printf("erase flash failed....\r\n");
		} 
		
		HAL_FLASH_Lock();
		
		return HAL_OK;
}

struct FlashData* GET_FLASH_DATA()
{
	uint8_t *items = Flash_Read_Byte((uint8_t *)&Flashdata, FLASH_USER_PAGE_ADDR, sizeof(struct FlashData));
	if(items[0] == 0xFF && items[1] == 0xFF) {
		return NULL;
	}
	
	return &Flashdata;
}

/*
HAL_StatusTypeDef FlashSetLoraServer(char *server, int size)
{
	if(size > FLASH_SERVICE_ID_LEN) {
		return HAL_ERROR;
	}
	struct FlashData *data = GET_FLASH_DATA();
	if(data == NULL) {
		data = &Flashdata;
	}
	memcpy(data->srvName, server, size);
	FlashErase(FLASH_USER_PAGE_ADDR);
	Flash_Write_Byte((uint8_t*)data, FLASH_USER_PAGE_ADDR, sizeof(struct FlashData));
	
	return HAL_OK;
}
*/

static const char base64_table[] = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

void GetUID(char output[17]) 
{
    const uint8_t *uid = (const uint8_t*)UID_BASE;
    uint32_t triple;
    int i, j = 0;
    
    // ???3??(24?)???
    for(i = 0; i < 12; i += 3) {
        // ??3???32??
        triple = (uid[i] << 16) | (uid[i+1] << 8) | uid[i+2];
        
        // ???4?Base64??
        output[j++] = base64_table[(triple >> 18) & 0x3F];
        output[j++] = base64_table[(triple >> 12) & 0x3F];
        output[j++] = base64_table[(triple >> 6)  & 0x3F];
        output[j++] = base64_table[triple & 0x3F];
    }
    output[16] = '\0';  // ???
}

HAL_StatusTypeDef FlashSetLoraConf(char *server, uint8_t freq, uint8_t net, uint8_t baud, uint8_t power, uint8_t secret[], uint8_t sf, uint8_t cr)
{
	struct FlashData *data = GET_FLASH_DATA();
	if(data == NULL) {
		data = &Flashdata;
	}
	data->freq = freq;
	data->net = net;
	data->baud = baud;
	data->power = power;
	data->sf = sf;
	data->cr = cr;
	//printf("data->power: %d\r\n", power);
	memset(data->srvName, 0, SERVER_LEN);
	memset(data->secret, 0, SECRET_LEN);
	memcpy(data->srvName, server, SERVER_LEN-1);
	memcpy(data->secret, secret, SECRET_LEN);
	GetUID(data->self);
	
	FlashErase(FLASH_USER_PAGE_ADDR);
	Flash_Write_Byte((uint8_t*)data, FLASH_USER_PAGE_ADDR, sizeof(struct FlashData));
	
	return HAL_OK;
}

HAL_StatusTypeDef FlashSetTHConf(uint8_t status)
{
	struct FlashData *data = GET_FLASH_DATA();
	if(data == NULL) {
		data = &Flashdata;
	}
	
	data->th = status; //1 ok. 0 not write.
	FlashErase(FLASH_USER_PAGE_ADDR);
	Flash_Write_Byte((uint8_t*)data, FLASH_USER_PAGE_ADDR, sizeof(struct FlashData));
	
	return HAL_OK;

}